package com.yusheng.tr.multi.aspectj;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.yusheng.tr.multi.annotation.ReportLog;
import com.yusheng.tr.common.core.entity.multi.ReportLogEntity;
import com.yusheng.tr.common.core.entity.multi.StmtMngEntity;
import com.yusheng.tr.multi.enums.LogEnum;
import com.yusheng.tr.multi.service.IReportLogService;
import com.yusheng.tr.multi.service.IStmtMngService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import me.codeleep.jsondiff.common.model.Defects;
import me.codeleep.jsondiff.common.model.JsonCompareResult;
import me.codeleep.jsondiff.common.model.JsonComparedOption;
import me.codeleep.jsondiff.common.model.TravelPath;
import me.codeleep.jsondiff.core.DefaultJsonDifference;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Method;
import java.util.*;

@Aspect
@Component
@Slf4j
public class ReportLogAspectj {

    @Autowired
    private IStmtMngService stmtMngService;

    @Autowired
    private IReportLogService reportLogService;

    /**
     * -- GETTER --
     *  获取HttpServletRequest对象
     */
    @Getter
    @Resource
    private HttpServletRequest httpServletRequest;

    @Pointcut("@annotation(com.yusheng.tr.multi.annotation.ReportLog)")
    public void serviceAspect() {
        log.info("****{}", "开启切面");
    }

    /**
     * @Description 前置通知  用于拦截Controller层记录用户的操作
     */

    @Around(value = "serviceAspect()")
    public Object around(ProceedingJoinPoint joinPoint) {
        try {
            getServiceMethodDescription(joinPoint);
            return joinPoint.proceed();
        } catch (Throwable e) {
            log.info("*****{}", e.getMessage());
        }
        return null;
    }

    public void getServiceMethodDescription(ProceedingJoinPoint joinPoint) throws Throwable {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class<?> targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        for (Method method : methods) {
            log.info("******73{}", method.getName());
            if (method.getName().equals(methodName)) {
                log.info("******75{}", method.getName());
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    ReportLogEntity operLog = new ReportLogEntity();
                    // 设置action动作
                    String type = method.getAnnotation(ReportLog.class).logType().toString();
                    operLog.setOperationType(type);
                    // 设置标题
                    String stmtId = parseSpel(method, arguments, method.getAnnotation(ReportLog.class).title(), String.class, null);
                    log.info("*******83{},", stmtId);
//                    log.info("*********joinPoint:{}",JSON.toJSONString(joinPoint.proceed()));
//                    Map map = JSONObject.parseObject(JSON.toJSONString(joinPoint.proceed()),Map.class);

                    // 设置IP地址
                    HttpServletRequest request = getHttpServletRequest();
                    String clientIp = getClientIp(request);
                    operLog.setIpAddress(clientIp);

                    operLog.setStmtId(StringUtils.isEmpty(stmtId) ? "qqqqqqqq".toString() : stmtId);
                    String content = parseSpel(method, arguments, method.getAnnotation(ReportLog.class).content(), String.class, null);
                    log.info("*******88{},", content);
                    operLog.setNewContent(content);
                    // 获取订单编号
//                    StmtMngEntity stmtMng = stmtMngService.getById(stmtId);
//                    String orderNum = stmtMng.getOrderNum();
//                    operLog.setStmtId(orderNum);
                    String orderNum = getOrderNum(stmtId);
                    operLog.setStmtId(orderNum);

                    if (type.equals(LogEnum.UPDATE.toString())) {
                        StmtMngEntity entity = stmtMngService.getById(stmtId);
                        if (entity != null) {
                            operLog.setOldContent(entity.getStmtContent());
                        }
                        operLog.setDiffContent(duibi(JSONObject.parseObject(operLog.getOldContent()), JSONObject.parseObject(content)));
                    }
                    reportLogService.saveOrUpdate(operLog);
                }
            }
        }
    }

    public String getOrderNum(String orderNum) {
        StmtMngEntity stmtMng = stmtMngService.getById(orderNum);
        return stmtMng == null ? "" : stmtMng.getOrderNum();
    }

    /**
     * 解析表达式
     */
    public static <T> T parseSpel(Method method, Object[] arguments, String spel, Class<T> clazz, T defaultResult) {
        ExpressionParser parser = new SpelExpressionParser();
        StandardReflectionParameterNameDiscoverer discoverer = new StandardReflectionParameterNameDiscoverer();
        String[] params = discoverer.getParameterNames(method);
        EvaluationContext context = new StandardEvaluationContext();
        for (int len = 0; len < Objects.requireNonNull(params).length; len++) {
            context.setVariable(params[len], arguments[len]);
        }
        try {
            Expression expression = parser.parseExpression(spel);
            log.info("*******118{}", expression);
            return expression.getValue(context, clazz);
        } catch (Exception e) {
            log.info("*******121:{}", JSON.toJSONString(e.getMessage()));
            return defaultResult;
        }
    }

    public String duibi(JSONObject s, JSONObject s1) {
        JsonComparedOption jsonComparedOption = new JsonComparedOption().setIgnoreOrder(true);
        JsonCompareResult jsonCompareResult = new DefaultJsonDifference()
                .option(jsonComparedOption)
                .detectDiff(s, s1);
        if (jsonCompareResult != null) {
            List<Defects> defects = jsonCompareResult.getDefectsList();
            if (!CollectionUtils.isEmpty(defects)) {
                List<Map<String, Object>> mapList = new ArrayList<>();
                defects.forEach(df -> {
                    TravelPath path = df.getTravelPath();
                    Map<String, Object> map = new HashMap<>();
                    map.put("filed", path.getAbstractTravelPath().split("\\.")[1]);
                    map.put("before", df.getExpect());
                    map.put("after", df.getActual());
                    mapList.add(map);
                });
                return JSON.toJSONString(mapList);
            }
        }
        return JSON.toJSONString(jsonCompareResult);
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip != null && ip.length() != 0 && !ip.equalsIgnoreCase("unknown")) {
            // 多个IP地址通过逗号分隔，只保留最后一个IP地址
            String[] ips = ip.split(",");
            ip = ips[ips.length - 1].trim();
        } else {
            ip = request.getHeader("Proxy-Client-IP");
            if (ip == null || ip.length() == 0 || ip.equalsIgnoreCase("unknown")) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || ip.equalsIgnoreCase("unknown")) {
                ip = request.getRemoteAddr();
            }
        }
        return ip;
    }
}


